/*******************************************************************************
 * Copyright (c) 2009 Kevin Bierhoff and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Kevin Bierhoff - initial API and implementation
 *******************************************************************************/
package com.googlecode.twelfdevtools.twelfast;

import com.googlecode.twelfdevtools.model.ITwelfConstant;

public class Binder extends AbstractTwelfAstVisitor<Boolean> {
	
	private final ITwelfTermContext ctx;
	private final ITwelfConstant constant;

	public Binder(ITwelfTermContext ctx, ITwelfConstant constant) {
		super();
		this.ctx = ctx;
		this.constant = constant;
	}

	private void bind(AbstractIdentifier node) {
		ITwelfBinding b = ctx.getBinding(node.getId());
		if(b != null)
			node.setBinding(b);
	}

	public Boolean app(Application node) {
		doDispatch(node.getLhs());
		doDispatch(node.getRhs());
		return null;
	}

	public Boolean arrow(Arrow node) {
		doDispatch(node.getLhs());
		doDispatch(node.getRhs());
		return null;
	}

	public Boolean ascr(Ascription node) {
		doDispatch(node.getTerm());
		doDispatch(node.getAscription());
		return null;
	}

	public Boolean constDecl(ConstantDeclaration node) {
		// TODO bind declared constant right...
		doDispatch(node.getConst());
		doDispatch(node.getTerm());
		doDispatch(node.getValue());
		return null;
	}

	public Boolean lambda(final Lambda node) {
		// extend context
		final String var = node.getId().getId();
		final String varTerm = node.getIdTerm().toString();
		ITwelfTermContext ctx = new ITwelfTermContext() {
			// TODO could populate lazily
			private final VariableBinding binding =
				new VariableBinding(node, var, "[" + var + ":" + varTerm + "]", constant);

			public ITwelfBinding getBinding(String id) {
				if(var.equals(id))
					return binding;
				return Binder.this.ctx.getBinding(id);
			}
		};
		// bind subnodes with extended context
		Binder b = new Binder(ctx, constant);
		b.doDispatch(node.getId());
		b.doDispatch(node.getIdTerm());
		b.doDispatch(node.getTerm());
		return null;
	}

	public Boolean pi(final Pi node) {
		// extend context
		final String var = node.getId().getId();
		final String varTerm = node.getIdTerm().toString();
		ITwelfTermContext ctx = new ITwelfTermContext() {
			// TODO could populate lazily
			private final VariableBinding binding =
				new VariableBinding(node, var, "{" + var + ":" + varTerm + "}", constant);
			
			public ITwelfBinding getBinding(String id) {
				if(var.equals(id))
					return binding;
				return Binder.this.ctx.getBinding(id);
			}
		};
		// bind subnodes with extended context
		Binder b = new Binder(ctx, constant);
		b.doDispatch(node.getId());
		b.doDispatch(node.getIdTerm());
		b.doDispatch(node.getTerm());
		return null;
	}

	public Boolean type(Type node) {
		// no binding for keyword "type"
		return null;
	}

	public Boolean id(Identifier node) {
		bind(node);
		return null;
	}

	public Boolean wildcard(Wildcard node) {
		throw new IllegalStateException("Cannot bind wildcard");
	}

}
